#include <stdlib.h>
#include <stdio.h>
#include "proto.h"
#include "server.h"
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include "../include/mlib.h"
#include "list.h"
#include "channel.h"
#include <net/if.h>


#define PTRMAXNUM 10

/*
 *  -M      Specify a multicast group
 *  -P      designated port
 *  -F      Front desk operation
 *  -D      designate media library location
 *  -H      display help information
 * 
 * 
 * */

struct server_conf_st server_conf = {
    .rcvport = DEFAULT_RCVPORT,
    .mgroup = DEFAULT_MGROUP,
    .runmode = RUN_DAEMON,
    .media_dir = DEFAULT_MEDIADIR,
    .ifname = DEFAULT_IF
    };

int sfd;
struct ip_mreqn mreqn;
struct sockaddr_in sndaddr;
void *freeptr[PTRMAXNUM];
int free_ptrnum;
static void printf_help()
{
    printf(" * -M     --mgroup              指定多播组\n\
                    *  -P      --port                      指定接受端口\n\
                    *  -F      --player                  指定前台运行\n\
                    *  -D                                       指定媒体库文件目录\n\
                    *  -H      --help                      显示帮助* \n");
}
/* 把当前进程设置为守护进程*/
static int daemonize() // create daemon process
{
    pid_t pid;
    int fd;
    pid = fork();
    if (pid < 0)
    {
        syslog(LOG_ERR, "fork():%s", strerror(errno));
        perror("fork()");
        exit(1);
    }
    if (pid > 0)
    {
        exit(0);
    }
    fd = open("/dev/null", O_RDWR);
    if (fd < 0)
    {
        syslog(LOG_ERR, "open():%s", strerror(errno));
        perror("open()");
        exit(1);
    }
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2); //脱离标准终端
    if (fd > 2)  //防止内存泄漏
    {
        close(fd);
    }
    setsid();
    if (chdir("/") < 0) //把守护进程指定到根目录
    {
        syslog(LOG_ERR, "chdir()%s", strerror(errno));
    }
    umask(0);
    return 0;
}

static void daemon_exit() //用于被信号终止之后调用
{
    for(int i  = 0; i < free_ptrnum; i++)
    {
        free(freeptr[i]);
    }
    thr_list_destroy();
    syslog(LOG_INFO, "daemon_exit():%s", strerror(errno));
    closelog();
    close(sfd);
    exit(0);
}

static int socket_init()
{
    sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd < 0)
    {
        syslog(LOG_ERR, "socket():%s", strerror(errno));
        exit(1);
    }

    inet_pton(AF_INET, server_conf.mgroup, &mreqn.imr_multiaddr);
    inet_pton(AF_INET, "0.0.0.0", &mreqn.imr_address);
    mreqn.imr_ifindex = if_nametoindex("ens33");
    if (setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0) //man 7 ip  把当前sdf加入到多播组
    {
        syslog(LOG_ERR, "setsockopt():%s", strerror(errno));
        exit(1);
    }

    sndaddr.sin_family = AF_INET;
    sndaddr.sin_port = htons(atoi(server_conf.rcvport)); //字序一定要转换
    inet_pton(AF_INET, server_conf.mgroup, &sndaddr.sin_addr.s_addr);
    return 0;
}

int main(int argc, char **argv)
{
    int c;

    struct sigaction sa;
    sa.sa_handler = daemon_exit;
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGQUIT);
    sigaddset(&sa.sa_mask, SIGTERM);

    sigaction(SIGTERM, &sa, NULL); // 对指定信号处理
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL);

    openlog("netradio", LOG_PID | LOG_PERROR, LOG_DAEMON); //打开系统日志

    /*cmd _ analyse*/
    while (1)
    {
        c = getopt(argc, argv, "M:P:FD:H");
        if (c < 0)
        {
            break;
        }
        switch (c)
        {
        case 'M':
            server_conf.mgroup = optarg;
            break;
        case 'P':
            server_conf.rcvport = optarg;
            break;
        case 'F':
            server_conf.runmode = RUN_FOREGROUND;
            break;
        case 'D':
            server_conf.media_dir = optarg;
            break;
        case 'H':
            printf_help();
            exit(0);
            break;
        default:
            abort();
            break;
        }
    }

    /*come ture daemon process */
    if (server_conf.runmode == RUN_DAEMON)
    {
        if (daemonize() != 0)
        {
            perror("daemonize");
        }
    }
    else if (server_conf.runmode == RUN_FOREGROUND)
    {
    }
    else
    {
        syslog(LOG_ERR, "EINVAL server_conf.runmode.");
        fprintf(stderr, "EINVAL\n");
        exit(1);
    }

    /* initialize socket  */
    socket_init();

    /* obtain channel information*/
    struct msg_list_st *list = NULL;  //节目单总列表
    int list_num = 0;  //节目单列表里频道个数
    int err;
    err = mlib_getchnlist(&list, &list_num); //获取回填
    if (err)
    {
        syslog(LOG_ERR, "mlib_getchnlist()%s", strerror(errno));
        exit(1);
    }

 struct msg_listentry_st *ptr;
    #ifdef DEBUG
    printf("list_num = %d\n", list_num);
   /*****变长结构体的一般处理方式********************/
    int m = 0;
    for (ptr = list->entry; m < list_num; ptr = (void *)((char *)ptr) + ntohs(ptr->len))
    {
        m++;
        printf("channel:%d:%s", ptr->chnid, ptr->desc);
    }

    #endif // DEBUG

    /*  create program list pthread*/
    err = thr_list_create(list, list_num);
    if (err)
    {
        syslog(LOG_ERR, "thr_list_create()%s", strerror(errno));
        exit(1);
    }

    /*  create chanel  pthread*/
     int i;
     i = 0;
    for (ptr = list->entry; i < list_num; ptr = (void *)((char *)ptr) + ntohs(ptr->len))
    {
        i++;
        err = thr_channel_create(ptr);
        if (err)
        {
            syslog(LOG_ERR, "thr_channel_create()%s", strerror(errno));
            exit(1);
        }
    }


     freeptr[0] = ptr;
     free_ptrnum = 1;


    while (1)
    {
        pause();
    }
}